<?php
declare (strict_types = 1);

namespace thinkless\common;

use exception\FriendlyException;
use util\ClassAttrParser;
use Error;
use think\Model;

/**
 * 模型
 */
abstract class BaseModel extends Model {

    protected static $parsers = [];
    /**
     * @var ClassAttrParser
     */
    private $parser = null;
    private $options = null;

    public function __construct($data = []) {
        if (!isset(static::$parsers[static::class])) {
            static::$parsers[static::class] = ClassAttrParser::parser(static::class);
        }
        $this->parser = static::$parsers[static::class];
        $this->table = empty($this->table) ? $this->parser->getMetaAttr('Table') : $this->table;
        if(!$this->table) throw new Error("模型类必须要有Table属性");
        parent::__construct($data);
    }

    public function table($alias = '') {
        $name = $this->table;
        return $alias ? ($name . ' ' . $alias) : $name; 
    }

    public function paginate($pageSize = 0) {
        if(empty($pageSize)) {
            $pageSize = input('get.page_size') ?? 10;
        }
        /**
         * @var think\Paginator
         */
        $paginate = $this->query->paginate($pageSize);
        $data = [];
        foreach ($paginate as $item) {
            $data[] = $this->outputFormat($item);
        }
        return [
            "total" => $paginate->total(),
            "per_page" => $paginate->listRows(),
            "current_page" => $paginate->currentPage(),
            "last_page" => $paginate->lastPage(),
            "data" => $data,
        ];
    }

    public function outputFormat($item = null) {
        if($item == null) $item = $this;
        // if(isset($item['status'])) $item['status_name'] = $this->mapStatusName($item['status']);
        $options = $this->getOptions();
        foreach ($options as $key => $option) {
            if(isset($item[$key])) {
                foreach ($option as $o) {
                    if(isset($o['label']) && $o['value'] == $item[$key]) $item[$key.'_name'] = $o['label'];
                }
            }
        }
        return $item;
    }

    public static function getUIOptions($key) {
        $clazz = get_called_class();
        $class = new $clazz();
        $options = $class->getOptions($key);
        $result = [];
        foreach ($options as $o) {
            $result[] = [ 'label' => $o['label'], 'value' => $o['value'] ];
        }
        return $result;
    }

    public function getOptions($key = '') {
        $options = $this->parseOptions();
        if($key) return isset($options[$key]) ? $options[$key] : [];
        return $options;
    }

    protected function parseOptions() {
        if($this->options == null) {
            $constants = $this->parser->parseConstants();
            $options = [];
            // 自动填充Option类型
            foreach ($constants as $properties) {
                foreach ($properties as $prop) {
                    if(!isset($prop['ATTR']) && $prop['ATTR'] != 'Option') continue;
                    if(!isset($prop['key']) || !isset($prop['label'])) throw new Error("Option的key和label必传");
                    if(!isset($options[$prop['key']])) {
                        $options[$prop['key']] = [];
                    }
                    $options[$prop['key']][] = $prop;
                }
            }
            $this->options = $options;
        }
        return $this->options;
    }

    /**
     * @return Object|null
     */
    public function findOne($id, $fields = []) {
        return $this->findOneByKey('id', $id, $fields);
    }

    /**
     * @return Object
     */
    public function mustFindOne($id, $msg = '没找到', $fields = []) {
        $item = $this->findOne($id, $fields);
        if(empty($item)) throw new FriendlyException($msg);
        return $item;
    }

    public function findOneByKey($key, $id, $fields = []) {
        return $this->where($key, $id)->field(empty($fields) ? '*': $fields)->find();
    }

    public function getByKey($key, $value) {
        if (empty($value)) return null;
        return $this->where([$key => $value])->find();
    }

    /**
     * @param $id
     * @return bool
     */
    public function hasById($id){
        return $this->hasByKey('id', $id);
    }

    /**
     */
    public function hasByKey($key, $value){
        if (empty($key) || empty($value)) return false;
        $data = $this->where($key, $value)->value($key);
        if(!$data) return false;
        return true;
    }
}